/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License. You may obtain a copy of the License at
	http://www.mozilla.org/MPL/
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import com.bourre.commands.Command;
import com.bourre.commands.Delegate;
import com.bourre.core.IAccessor;
import com.bourre.data.collections.IndexedArray;
import com.bourre.events.EventBroadcaster;
import com.bourre.events.EventType;

import fever.data.file.FileFilter;
import fever.utils.Stringifier;
import fever.utils.StringUtil;

import fvaswing.components.filechooser.events.FvFileChooserEvent;
import fvaswing.components.filechooser.events.FvFileChooserListener;
import fvaswing.components.filechooser.factory.FvChooserStrategy;
import fvaswing.components.filechooser.FvFileChooserResources;
import fvaswing.components.filechooser.FvFileView;
import fvaswing.components.filechooser.mvc.FvFileChooserController;

/**
 * <strong>FvFileChooser</strong> provides a simple mechanism for the user to 
 * choose a file for save or open process.
 * 
 * <p>Remoting process used <strong>Pixlib remoting implementation</strong> which 
 * can be found on : http://www.tweenpix.net/tmp/alpha_remoting.rar <br />
 * <strong>Pixlib remoting</strong> still in alpha version but it's already robust.<br />
 * Thanks to author Francis Bourre.
 * 
 * <p>By default, the remoting service start his search from the root web folder.<br />
 * If you want to constraint user to a specific folder ( as his root folder ), modify 
 * the {@code $start_path} property in the <strong>DirectoryService</strong> file.
 * 
 * <p>Example
 * {@code
 * public function run() : Void
 * {
 *	var chooser = new FvFileChooser();
 *	chooser.addEventListener( FvFileChooser.ON_FILE_CHOOSER_RESULT, this, _onResult );
 *	
 *	chooser.showOpenDialog();
 * }
 * }
 * 
 * @author Romain Ecarnot
 */
class fvaswing.components.FvFileChooser
{
	//-------------------------------------------------------------------------
	// Events definition
	//-------------------------------------------------------------------------
	
	/** Dispatches when user close the chooser dialog. */
	public static var onFileChooserEVENT : EventType = FvFileChooserEvent.onFileChooserEVENT;
	
	
	//-------------------------------------------------------------------------
	// Constants definition
	//-------------------------------------------------------------------------
	
	/** ID used when user select a file */
	public static var APPROUVE_OPTION : Number = 1;
	
	/** ID used when user cancel operation. */
	public static var CANCEL_OPTION : Number = 0;
	
	/** ID used when error occured in chooser. */
	public static var ERROR_OPTION : Number = -1;
	 
	
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private static var _instance : FvFileChooser;
	
	private static var _basePath : String;
	
	private var _broadcaster : EventBroadcaster;
	private var _controller : FvFileChooserController;
	private var _accessor : IAccessor;	private var _selectionHandler : Delegate;
	
	
	//-------------------------------------------------------------------------
	// Public Properties
	//-------------------------------------------------------------------------
	
	/** Base path to map AMFPHP start folder definition. */
	public static function get BASE_PATH() : String { return _basePath; }
	public static function set BASE_PATH( path  : String ) : Void
	{
		_basePath = StringUtil.trim( path, StringUtil.EMPTY_CHARS + '/' );
	}
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Returns singleton instance of {@link FvFileChooser} class.
	 */
	public static function getChooser() : FvFileChooser
	{
		if( !_instance ) _instance = new FvFileChooser();
		return _instance;	
	}
	
	/**
	 * Constructor.
	 */
	public function FvFileChooser()
	{
		super();
		
		FvFileView.registerDefaultViews();
		
		_broadcaster = new EventBroadcaster( this );
		_controller = new FvFileChooserController( this );
	}
	
	/**
	 * Adds passed-in {@code listener} listener for receiving all events.
	 * 
	 * @param oL {@link fvaswing.components.filechooser.events.FvFileChooserListener} object
	 */
	public function addListener( listener : FvFileChooserListener ) : Void
	{
		_broadcaster.addListener( listener );	
	}
	
	/**
	 * Removes passed-in {@code listener} listener for receiving all events.
	 * 
	 * @param listener Listener object.
	 */
	public function removeListener( listener : FvFileChooserListener ) : Void
	{
		_broadcaster.removeListener( listener );	
	}
	
	/**
	 * Adds passed-in {@code listener} for receiving passed-in {@code type} event.
	 * 
	 * <p>Example
	 * {@code
	 *   var oEB : EventBroadcaster = new EventBroadcaster(this);
	 *   oEB.addEventListener( myClass.onSometingEVENT, myFirstObject);
	 *   oEB.addEventListener( myClass.onSometingElseEVENT, this, _onSomethingElse);
	 *   oEB.addEventListener( myClass.onSometingElseEVENT, this, Delegate.create(this, _onSomething) );
	 * }
	 * 
	 * @param type Name of the Event.
	 * @param listener Listener object.
	 */
	public function addEventListener( type : EventType, listener ) : Void
	{
		_broadcaster.addEventListener.apply( _broadcaster, arguments );
	}
	
	/**
	 * Removes passed-in {@code listener} that suscribed for passed-in {@code type} event.
	 * 
	 * <p>Example
	 * {@code
	 *   var oEB : EventBroadcaster = new EventBroadcaster(this);
	 *   oEB.removeEventListener( myClass.onSometingEVENT, myFirstObject);
	 *   oEB.removeEventListener( myClass.onSometingElseEVENT, this);
	 * }
	 * 
	 * @param type Name of the Event.
	 * @param listener Listener object.
	 */
	public function removeEventListener( type : EventType, listener ) : Void
	{
		_broadcaster.removeEventListener( type, listener );		
	}
	
	/**
	 * Unsuscribes all listeners.
	 */
	public function releaseEvent() : Void
	{
		_broadcaster.removeAllListeners();
	}
	
	/**
	 * Connects <strong>Pixlib Accessor</strong> to chooser.
	 *
	 * <p>When chooser is closed, result is automatically sent to accessor instance.
	 * 
	 * <p>Example
	 * {@code
	 *   var input : JTextField = new JTextField( "", 15 );
	 *   chooser.connect( AccessorFactory.getInstance( input, input.setText, input.getText ) );
	 *   chooser.showOpenDialog();
	 * }
	 * 
	 * @param accessor IAccessor instance ( take a look at Pixlib bourre.core package )
	 */
	public function connect( accessor : IAccessor ) : Void
	{
		_accessor = accessor;
	}
	
	/**
	 * Releases accessor connection.
	 */
	public function disconnect() : Void
	{
		_accessor = null;
	}
	
	/**
	 * Handler is call when user click on approuve button.
	 */
	public function setFileSelectionHandler( scope, callback : Function ) : Void
	{
		_selectionHandler = new Delegate( scope, callback );
	}
	
	/**
	 * Releases file selection handler.
	 */
	public function releaseFileSelectionHandler() : Void
	{
		_selectionHandler = null;	
	}
	
	/**
	 * Show an '<strong>Open</strong>' modal dialog containing the file chooser. 
	 * 
	 * <p>When receive the {@link fvaswing.components.filechooser.events.FvFileChooserEvent} 
	 * you can check the {@link fvaswing.components.filechooser.events.FvFileChooserEvent#getResult} 
	 * properties to know operation success.<br /> 
	 * These propertie can be
	 * <ul>
	 *   <li>{@link #APPROVE_OPTION} if the user approved</li>
	 *   <li>{@link #CANCEL_OPTION} if the user cancelled it</li>
	 *   <li>{@link #ERROR_OPTION}, which means an unanticipated error occurred</li>
	 * </ul>
	 */
	public function showOpenDialog() : Void
	{
		_controller.showDialog( FvFileChooserResources.OPEN );
	}	
	
	/**
	 * Show a '<strong>Save</strong>' modal dialog containing the file chooser. 
	 * 
	 * <p>When receive the {@link fvaswing.components.filechooser.events.FvFileChooserEvent} 
	 * you can check the {@link fvaswing.components.filechooser.events.FvFileChooserEvent#getResult} 
	 * properties to know operation success.<br /> 
	 * These propertie can be
	 * <ul>
	 *   <li>{@link #APPROVE_OPTION} if the user approved</li>
	 *   <li>{@link #CANCEL_OPTION} if the user cancelled it</li>
	 *   <li>{@link #ERROR_OPTION}, which means an unanticipated error occurred</li>
	 * </ul>
	 * 
	 * <p>{@link #setMultiSelectionEnabled} is reset to {@code false}
	 */
	public function showSaveDialog() : Void
	{
		setMultiSelectionEnabled( false );
		
		_controller.showDialog( FvFileChooserResources.SAVE );
	}
	
	/**
	 * Show a '<strong>Browse folder</strong>' modal dialog containing the file chooser. 
	 * 
	 * <p>When receive the {@link fvaswing.components.filechooser.events.FvFileChooserEvent} 
	 * you can check the {@link fvaswing.components.filechooser.events.FvFileChooserEvent#getResult} 
	 * properties to know operation success.<br /> 
	 * These propertie can be
	 * <ul>
	 *   <li>{@link #APPROVE_OPTION} if the user approved</li>
	 *   <li>{@link #CANCEL_OPTION} if the user cancelled it</li>
	 *   <li>{@link #ERROR_OPTION}, which means an unanticipated error occurred</li>
	 * </ul>
	 * 
	 * <p>{@link #setMultiSelectionEnabled} is reset to {@code false}
	 */
	public function showFolderBrowser(): Void
	{
		setMultiSelectionEnabled( false );
		
		_controller.showDialog( FvFileChooserResources.BROWSE );
	}
	
	/**
	 * Show a '<strong>custom</strong>' modal dialog containing the file chooser. 
	 * 
	* <p>When receive the {@link fvaswing.components.filechooser.events.FvFileChooserEvent} 
	 * you can check the {@link fvaswing.components.filechooser.events.FvFileChooserEvent#getResult} 
	 * properties to know operation success.<br /> 
	 * These propertie can be
	 * <ul>
	 *   <li>{@link #APPROVE_OPTION} if the user approved</li>
	 *   <li>{@link #CANCEL_OPTION} if the user cancelled it</li>
	 *   <li>{@link #ERROR_OPTION}, which means an unanticipated error occurred</li>
	 * </ul>
	 * 
	 * @param buttonCaption Caption of the 'ok' button
	 */
	public function showDialog( buttonCaption : String ) : Void
	{
		_controller.showDialog( FvFileChooserResources.CUSTOM );
	}
	
	/**
	 * Defines strategy to use in Chooser dialog.
	 */
	public function setChooserStrategy( strategy : FvChooserStrategy ) : Void
	{
		_controller.setChooserStrategy( strategy );
	}
	/**
	 * Sets whether multiple files can be selected at once. 
	 * 
	 * <p>By default, a user can choose only one file.
	 */
	public function setMultiSelectionEnabled( enabled : Boolean ) : Void
	{
		_controller.setMultiSelectionEnabled( enabled );	
	}
	
	/**
	 * Returns whether multiple files can be selected at once. 
	 */
	public function isMultiSelectionEnabled() : Boolean
	{
		return _controller.isMultiSelectionEnabled();
	}
	
	/**
	 * Sets whether upload feature is enabled {@code true} or not 
	 * {@code false}
	 * 
	 * <p>Default is {@code false}
	 */
	public function setUploadEnabled( enabled : Boolean ) : Void
	{
		_controller.setUploadEnabled( enabled );
	}
	
	/**
	 * Returns {@code true} if upload feature is enabled in current 
	 * {@link FvFileChooser} component.
	 */
	public function isUploadEnabled() : Boolean
	{
		return _controller.isUploadEnabled( );	
	}
	
	/**
	 * Sets whether preview feature is enabled {@code true} or not 
	 * {@code false}
	 * 
	 * <p>Default is {@code false}
	 */
	public function setPreviewEnabled( enabled : Boolean ) : Void
	{
		_controller.setPreviewEnabled( enabled );	
	}
	
	/**
	 * Returns {@code true} if preview feature is enabled in current 
	 * {@link FvFileChooser} component.
	 */
	public function isPreviewEnabled( ) : Boolean
	{
		return _controller.isPreviewEnabled( );		
	}
	
	/**
	 * Add new type to preview loader. 
	 */
	public function addPreviewCommand( type : String, previewCommand : Command ) : Void
	{
		_controller.addPreviewCommand( type, previewCommand );
	}
	
	/**
	 * Add new type to preview loader. 
	 */
	public function addPreviewType( type : String, scopeMethod, parsingMethod : Function ) : Void
	{
		_controller.addPreviewCommand( type, new Delegate( scopeMethod, parsingMethod ) );
	}
	
	/**
	 * Sets whether the 'AcceptAll' file filter is used as an allowable choice in 
	 * the choosable filter list; the default value is {@code true}.
	 */
	public function setAcceptAllFileFilterUsed( enabled : Boolean ) : Void
	{
		_controller.setAcceptAllFileFilterUsed( enabled );
	}
	
	/**
	 * Returns whether the' AcceptAll' file filter is used as an allowable choice in 
	 * the choosable filter list.
	 */
	public function isAcceptAllFileFilterUsed() : Boolean
	{
		return _controller.isAcceptAllFileFilterUsed();
	}
	
	/**
	 * Sets the file chooser's primary file filter.
	 */
	public function setFileFilter( filter : FileFilter ) : Void
	{
		_controller.setFileFilter( filter );
	}
	
	/**
	 * Returns the file chooser's primary file filter.
	 */
	public function getFileFilter() : FileFilter
	{
		return _controller.getFileFilter();	
	}
	
	/**
	 * Returns file chooser filter list.
	 */
	public function getChoosableFileFilters() : IndexedArray
	{
		return _controller.getChoosableFileFilters();
	}
	
	/**
	 * Adds a new custom file filter.
	 */
	public function addChoosableFileFilter( filter : FileFilter ) : Void
	{
		_controller.addChoosableFileFilter( filter );	
	}
	
	/**
	 * Removes passed-in {@link fever.data.file.FileFilter} 
	 * from file chooser filter list.
	 * 
	 * @return {@code true} is filter is removed, otherwise {@code false}
	 */
	public function removeChoosableFileFilter( filter : FileFilter ) : Boolean
	{
		return _controller.removeChoosableFileFilter( filter );
	}
	
	/**
	 * Resets file chooser filter list to primary filter list.
	 */
	public function resetChoosableFileFilters() : Void
	{
		_controller.resetChoosableFileFilters();	
	}
	
	/**
	 * Returns the default 'AcceptAll' filter instance.
	 */
	public function getAcceptAllFileFilter() : FileFilter
	{
		return _controller.getAcceptAllFileFilter();
	}
	
	/**
	 * Broadcasts an {@link #ON_FILE_CHOOSER_RESULT} event type.
	 * 
	 * @param type  Possible values are 
	 * <ul>
	 *   <li>{@link #APPROUVE_OPTION}</li>
	 *   <li>{@link #CANCEL_OPTION}</li>
	 *   <li>{@link #ERROR_OPTION}</li>
	 * </ul>
	 * @param files Array structure with all selected files
	 * @param path Base path of selected files
	 */
	public function fireResult( type : Number, files : Array, path : String ) : Void
	{
		var e : FvFileChooserEvent = new FvFileChooserEvent( type, files, path );
		e.setTarget( this );
		
		if( _accessor && e.getSelectedFile().getPath() ) _accessor.setValue( e.getSelectedFile().getPath() );
		if( _selectionHandler != null )
		{
			_selectionHandler.setArguments( e );
			_selectionHandler.callFunction();
		}
		
		_broadcaster.broadcastEvent( e );
	}
	
	/**
	 * Returns string representation.
	 */
	public function toString() : String
	{
		return Stringifier.parse( this );
	}
}